package com.potenza.training.media.service;

import com.alibaba.nacos.common.utils.IoUtils;
import com.potenza.training.media.controller.MediaUploadController;
import com.potenza.training.media.dao.MediaFileMapeer;
import com.potenza.training.media.model.CheckChunkResult;
import com.potenza.training.media.model.MediaFile;
import com.potenza.training.media.model.Result;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

@Service
public class MediaUploadService {

    private final static Logger LOGGER = LoggerFactory.getLogger(MediaUploadController.class);
    //上传文件根目录
    @Value("${potenza-service-manage-media.upload-location}")
    String uploadPath;


    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    MediaFileMapeer mediaFileMapeer;

    //文件上传注册
    public Result register(String fileMd5,//文件唯一标识
                           String fileName,//文件名称
                           Long fileSize,//文件大小
                           String mimetype,//文件类型
                           String fileExt //文件是否存在
                           ) {
        System.out.println("register " + fileMd5
                +" fileName<"+fileName +"> fileSize<"+fileSize+">"
                +" mimetype<"+mimetype +"> fileExt<"+fileExt+">");

        //检查文件是否上传
        //1、 得到文件的路径
                               String fileFolderPath = this.getFileFolderPath(fileMd5);
                               System.out.println("文件所属目录的路径+"+fileFolderPath);

                                //文件的路径
                               String filePath = this.getFilePath(fileMd5, fileExt);

                               File file = new File(filePath);
                               //判断文件是否存在
                               boolean exists = file.exists();
          MediaFile mediaFile = mediaFileMapeer.selectById(fileMd5);
                        if(exists && mediaFile!=null){
                                 return new Result(true,"1","已经存在");
                                 //这里就是文件已经存在 的异常
                               }

                               // 文件命名规则
         // 根据文件md5得到文件路径
          //规则：
         // 一级目录：md5的第一个字符
         // 二级目录：md5的第二个字符
         // 三级目录：md5
          //文件名：md5+文件扩展名


                       //2、 不存在创建目录
                               File fileFolder = new File(fileFolderPath);
                               if(!fileFolder.exists()){
                                   //createFileFold(fileMd5);
                                   fileFolder.mkdirs();
                               }
                               //创建成功则返回
        return  new Result(true,"1","创建目录成功");//已经存在
    }


    ////检查块文件
    public CheckChunkResult checkchunk(String fileMd5,
                                       Integer chunk,
                                       Integer chunkSize) {
        System.out.println("checkchunk " + fileMd5 +" chunk<"+chunk +"> chunkSize<"+chunkSize+">");
        //得到块文件所在路径
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);

        File file = new File(chunkFileFolderPath + chunk);
        //块文件的文件名称以1,2,3..序号命名，没有扩展名

        boolean chunkFileExist = false;
        if(file.exists()){
            return new CheckChunkResult(true,"1","chunk<"+chunk+">存在",true);
        }else{
            return new CheckChunkResult(true,"2","chunk<"+chunk+">不存在",false);
        }
    }

    public Result uploadchunk(MultipartFile file,
                                      String fileMd5,
                                      Integer chunk) throws IOException {
        System.out.println("uploadchunk " + fileMd5 +" chunk<"+chunk +">");
      //检查分块的目录,如果不存在册自动创建
        //得到分块目录
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
        String chunkFilePath=chunkFileFolderPath+chunk;
        File fileFileFolder = new File(chunkFileFolderPath);
        if(!fileFileFolder.exists()){
            fileFileFolder.mkdirs();
        }
        //得到上传文件的输入流
        InputStream inputStream=null;
        FileOutputStream outputStream=null;
        try {
            inputStream=file.getInputStream();
            outputStream=new FileOutputStream(new File(chunkFilePath));
            IoUtils.copy(inputStream,outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //上传该块

        //保存到相应的目录下
        return  new Result(true,"1","上传块成功");
    }

    public Result mergechunks(String fileMd5,
                                      String fileName,
                                      Long fileSize,
                                      String mimetype,
                                      String fileExt) {
        System.out.println("mergechunks " + fileMd5 +" fileName<"+fileName +">");
        //1、获取块文件的路径
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
        File chunkfileFolder = new File(chunkFileFolderPath);
        //2、合并文件路径
        //2.1创建合并文件
        //分块文件的列表
        File[] files = chunkfileFolder.listFiles();
        //创建一个合并文件
        String filePath = this.getFilePath(fileMd5, fileExt);
        File mergeFile = new File(filePath);
        mergeFile=this.mergeFile(mergeFile,Arrays.asList(files));
      if(mergeFile ==null){
          return  new Result(false,"1","失败");

      }
     // 2.校验文件MD5值是否和前端传人的MD5一直
        if(!this.checkFileMd5(mergeFile,fileMd5)){
            MediaFile mediaFile = new MediaFile();
            mediaFile.setFileId(fileMd5);
            mediaFile.setFileName(fileName);
            mediaFile.setFilePath(filePath);
            mediaFile.setFileSize(fileSize);
            mediaFileMapeer.insert(mediaFile);
            return  new Result(false,"1","文件失败");
        }


        //2.2合并文件存在先删除再创建

        //3、合并文件

        //4、MD5的校验，合并后的文件 计算MD5与源文件MD5比较
        //DigestUtils.md5Hex();
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileName(fileName);
        mediaFile.setFilePath(filePath);
        mediaFile.setFileSize(fileSize);
        mediaFileMapeer.insert(mediaFile);
        //5、异步调用-->RabbitMQ,向MQ发送视频处理消息
        return  new Result(true,"1","合并块成功");

    }

    //校验文件的md5值
    private boolean checkFileMd5(File mergeFile,String fileMd5){
        if(mergeFile == null || StringUtils.isEmpty(fileMd5)){
            return false;
        }
        InputStream mergeFileInputstream = null;
        try {
            mergeFileInputstream = new FileInputStream(mergeFile);
            //得到文件的md5
            String mergeFileMd5 = DigestUtils.md5Hex(mergeFileInputstream);
            //比较md5
            if(fileMd5.equalsIgnoreCase(mergeFileMd5)){
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("checkFileMd5 error,file is:{},md5 is:{}",mergeFile.getAbsoluteFile(),fileMd5);
        }finally {
            try {
                mergeFileInputstream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
    //合并文件
    private File mergeFile(File mergeFile,List<File> chunkFiles){
        try {
            RandomAccessFile raf_write = new RandomAccessFile(mergeFile,"rw");
            //合并文件
            for(File chunkFile:chunkFiles){
                RandomAccessFile raf_read = new RandomAccessFile(chunkFile,"r");
                int len = -1;
                byte[] b = new byte[1024];
                while((len=raf_read.read(b))!=-1){
                    raf_write.write(b,0,len);
                }
                raf_read.close();
            }
            raf_write.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return mergeFile;
    }
    private  List<File>  getChunkFiles(File chunkfileFolder){
        File[] chunkFiles = chunkfileFolder.listFiles();
        List<File> chunkFileList = new ArrayList<File>(Arrays.asList(chunkFiles));
        Collections.sort(chunkFileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if(Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())){
                    return 1;
                }else{
                    return -1;
                }
            }
        });
        return chunkFileList;
    }


    private boolean createChunkFileFolder(String fileMd5){
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);
        if(!chunkFileFolder.exists()){
            //创建文件夹
            boolean mkdirs = chunkFileFolder.mkdirs();
            return mkdirs;
        }
        return true;
    }
    /**
     * 根据文件md5得到文件路径
     * 规则：
     * 一级目录：md5的第一个字符
     * 二级目录：md5的第二个字符
     * 三级目录：md5
     * 文件名：md5+文件扩展名
     * @param fileMd5 文件md5值
     * @param fileExt 文件扩展名
     * @return 文件路径
     */
    private String getFilePath(String fileMd5,String fileExt){
        String filePath = uploadPath+fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) +
                "/" + fileMd5 + "/" + fileMd5 + "." + fileExt;
        return filePath;
    }
    //得到文件目录相对路径，路径中去掉根目录
    private String getFileFolderRelativePath(String fileMd5,String fileExt){
        String filePath = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" +
                fileMd5 + "/";
        return filePath;
    }

    //找到文件路径
    private String getFileFolderPath(String fileMd5){
        String fileFolder = uploadPath+fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) +
                "/" + fileMd5 + "/";
        return fileFolder;
    }
    //得到块文件所在目录
    private String getChunkFileFolderPath(String fileMd5){
        String fileChunkFolderPath = getFileFolderPath(fileMd5) +"/" + "chunks" + "/";
        return fileChunkFolderPath;
    }


    private boolean createFileFold(String fileMd5){
        String fileFolderPath = getFileFolderPath(fileMd5);
        File fileFolder = new File(fileFolderPath);
        if(!fileFolder.exists()){
            //创建文件夹
            boolean mkdirs = fileFolder.mkdirs();
            return mkdirs;
        }
        return true;
    }
}
